package cn.iotnc.camera.util

import androidx.annotation.CheckResult
import cn.iotnc.camera.ui.main.MainActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext
import org.jetbrains.anko.AnkoLogger
import org.jetbrains.anko.info
import java.net.DatagramPacket
import java.net.InetAddress
import java.net.MulticastSocket
import java.net.NetworkInterface
import java.util.*
import kotlin.collections.ArrayList

const val BUFFER_SIZE = 2048
const val PORT = 2020

const val MULTI_ADDRESS = "239.255.9.25"

fun Int.getMulticastSocket() = MulticastSocket(this).apply {
    loopbackMode = true
    reuseAddress = true
    broadcast = true
    val group = InetAddress.getByName(MULTI_ADDRESS)
    AnkoLogger<MulticastSocket>().info { "$MULTI_ADDRESS isMulticastAddress: ${group.isMulticastAddress}" }
    joinGroup(group)
}

data class Message(
    val content: String,
    val ip: String = MULTI_ADDRESS
)

@CheckResult
@ExperimentalCoroutinesApi
fun MulticastSocket.receiveMessage(): Flow<Message> = callbackFlow<Message> {
    val buf = ByteArray(BUFFER_SIZE)
    val packet = DatagramPacket(buf, buf.size)
    withContext(Dispatchers.IO) {
        while (isActive) {
            try {
                receive(packet)
                val ip: String = packet.address.hostAddress
                val content: String = String(packet.data, 0, packet.length).trim()
                safeOffer(Message(content, ip))
            } catch (e: Exception) {
                AnkoLogger<MainActivity>().info { "receiveMessage error: $e" }
            }
        }
    }
    awaitClose {
        AnkoLogger<MainActivity>().info { "close socket...." }
        close()
    }
}.conflate()

private val buf = ByteArray(BUFFER_SIZE)
private val packet = DatagramPacket(buf, buf.size)
fun MulticastSocket.receiveMsg(): Message {
    receive(packet)
    val ip: String = packet.address.hostAddress
    val content: String = String(packet.data, 0, packet.length).trim()
    return Message(content, ip)
}

suspend fun MulticastSocket.sendMessage(message: Message) = withContext(Dispatchers.IO) {
    val data: ByteArray = message.content.toByteArray()
    val dataPacket = DatagramPacket(data, data.size, InetAddress.getByName(message.ip), PORT)
    send(dataPacket)
    AnkoLogger<MulticastSocket>().info { "sendMessage: $message" }
}

fun getLocalIPs(): ArrayList<String> {
    val ipLists: ArrayList<String> = ArrayList()
    val en: Enumeration<NetworkInterface> = NetworkInterface.getNetworkInterfaces()
    // 遍历所用的网络接口
    while (en.hasMoreElements()) {
        val networkInterface: NetworkInterface = en.nextElement() // 得到每一个网络接口绑定的所有ip
        val enumeration: Enumeration<InetAddress> = networkInterface.inetAddresses
        // 遍历每一个接口绑定的所有ip
        while (enumeration.hasMoreElements()) {
            val ip: InetAddress = enumeration.nextElement()
            if (!ip.isLoopbackAddress) {
                ipLists.add(ip.hostAddress)
            }
        }
    }
    return ipLists
}